// Copyright 2011 Google Inc. All Rights Reserved.

package udrpc

import (
	"bytes"
	"encoding/binary"
	"log"
	"os"
	"syscall"
)

const (
	sizeofInt32 = 4
)

// Buffer holds the data and handles to pass to Socket.
type Buffer struct {
	Buffer  *bytes.Buffer
	Handles *Handles
}

// NewBuffer returns a new instance of Buffer with given handles.
func NewBuffer(handles *Handles) *Buffer {
	return &Buffer{
		Buffer:  new(bytes.Buffer),
		Handles: handles,
	}
}

func (b *Buffer) packet() (p, oob []byte) {
	p = b.Buffer.Bytes()
	if b.Handles.Len() == 0 {
		return
	}
	buf := new(bytes.Buffer)
	size := b.Handles.Len() * sizeofInt32
	cmsg := syscall.Cmsghdr{
		Level: syscall.SOL_SOCKET,
		Type:  syscall.SCM_RIGHTS,
	}
	cmsg.SetLen(size + syscall.SizeofCmsghdr)
	binary.Write(buf, binary.LittleEndian, cmsg)

	err := b.Handles.saveTo(buf)
	if err != nil {
		log.Fatal("Could not save file handles to buffer: ", err)
	}

	return p, buf.Bytes()
}

func parseBuffer(p, oob []byte) (buf *Buffer) {
	h := new(Handles)
	buf = &Buffer{
		Buffer:  bytes.NewBuffer(p),
		Handles: h,
	}
	if len(oob) == 0 {
		return
	}
	r := bytes.NewBuffer(oob)
	for {
		var cmsg syscall.Cmsghdr
		err := binary.Read(r, binary.LittleEndian, &cmsg)
		if err != nil {
			log.Fatal("parseBuffer: Can't parse buffer. oob: ", oob)
		}
		if cmsg.Level != syscall.SOL_SOCKET ||
			cmsg.Type != syscall.SCM_RIGHTS {
			continue
		}
		dataSize := int(cmsg.Len) - syscall.SizeofCmsghdr
		cnt := dataSize / sizeofInt32
		for i := 0; i < cnt; i++ {
			var fd int32
			err := binary.Read(r, binary.LittleEndian, &fd)
			if err != nil {
				log.Fatal("parseBuffer: Unexpected end of file descriptors")
			}
			h.Add(os.NewFile(int(fd), "transferred-handle"))
		}
		if len(r.Bytes()) < syscall.SizeofCmsghdr {
			break
		}
	}
	return
}
